Skip to content

Add urql integration package#671

Merged
tombruijn merged 3 commits intomainfrom
add-urql-integration
Apr 10, 2026
Merged

Add urql integration package#671
tombruijn merged 3 commits intomainfrom
add-urql-integration

Conversation

@jeffkreeftmeijer
Copy link
Copy Markdown
Contributor

Based on the example implementation in https://github.com/appsignal/test-setups/tree/reproduce-graphql-error-react, this patch adds a new @appsignal/urql package that provides automatic GraphQL error reporting for applications using the urql GraphQL client.

The package exports a createAppsignalExchange function that creates a custom urql exchange to intercept all query and mutation results and automatically report errors to AppSignal without requiring changes to individual useQuery calls.

This provides a seamless integration for urql users to get complete visibility into GraphQL errors in their applications.

Usage

import { createClient, fetchExchange } from 'urql';
import Appsignal from '@appsignal/javascript';
import { createAppsignalExchange } from '@appsignal/urql';

const appsignal = new Appsignal({
  key: 'YOUR FRONTEND API KEY'
});

const client = createClient({
  url: 'https://api.example.com/graphql',
  exchanges: [createAppsignalExchange(appsignal), fetchExchange]
});

Testing

Use the https://github.com/appsignal/test-setups/tree/reproduce-graphql-error-react branch, and hook that up to a local checkout of the repository. I haven't been able to get the test-setup to fetch from GitHub directly.

@jeffkreeftmeijer jeffkreeftmeijer self-assigned this Mar 26, 2026
@jeffkreeftmeijer jeffkreeftmeijer added the feature A new feature for this component. label Mar 26, 2026
@backlog-helper
Copy link
Copy Markdown

backlog-helper bot commented Mar 26, 2026

✔️ All good!

New issue guide | Backlog management | Rules | Feedback

Comment thread packages/urql/src/index.ts Outdated
Add a new @appsignal/urql package that provides automatic GraphQL
error reporting for applications using the urql GraphQL client.

The package exports a createAppsignalExchange function that creates a
custom urql exchange to intercept all query and mutation results and
automatically report errors to AppSignal without requiring changes to
individual useQuery calls.

Error reports include:
- GraphQL query body as a parameter
- Endpoint URL as a tag
- Operation name and type as tags when available

This provides a seamless integration for urql users to get complete
visibility into GraphQL errors in their applications.
@backlog-helper

This comment has been minimized.

Comment thread packages/urql/src/index.ts
@backlog-helper

This comment has been minimized.

5 similar comments
@backlog-helper

This comment has been minimized.

@backlog-helper

This comment has been minimized.

@backlog-helper

This comment has been minimized.

@backlog-helper

This comment has been minimized.

@backlog-helper

This comment has been minimized.

@tombruijn tombruijn assigned lipskis and unassigned jeffkreeftmeijer Apr 7, 2026
@backlog-helper
Copy link
Copy Markdown

backlog-helper bot commented Apr 8, 2026


This is a message from the daily scheduled checks.

New issue guide | Backlog management | Rules | Feedback

@lipskis lipskis removed their request for review April 8, 2026 15:34
@lipskis lipskis force-pushed the add-urql-integration branch from f248c71 to 418e25c Compare April 8, 2026 18:07
Extract `reportGraphQLError` function and test it in isolation. This
allows testing Appsignal error handling logic without wiring up
`urql` exchange plumbing.
@lipskis lipskis force-pushed the add-urql-integration branch from 418e25c to c0fc547 Compare April 8, 2026 18:31
Comment thread packages/urql/src/index.ts Outdated
Comment on lines +72 to +74
if (operation?.operationName) {
span.setTags({ operationName: operation.operationName })
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't do anything in the test app. The structure of the operation value is like shown below.

So I suggest we remove it for now, as our sample tag value doesn't support partial matches at the moment. If we would collect all operation names and store them all in the same tag, you wouldn't be able to find anything for errors with more than one operation + name.

{
  "operation": {
    "key": 3871781863,
    "query": {
      "kind": "Document",
      "definitions": [
        {
          "kind": "OperationDefinition",
          "operation": "query",
          "name": {
            "kind": "Name",
            "value": "customInvalidOperation"
          },
          "variableDefinitions": [],
          "directives": [],
          "selectionSet": {
            "kind": "SelectionSet",
            "selections": [
              {
                "kind": "Field",
                "name": {
                  "kind": "Name",
                  "value": "nonExistentField"
                },
                "arguments": [],
                "directives": [],
                "selectionSet": {
                  "kind": "SelectionSet",
                  "selections": [
                    {
                      "kind": "Field",
                      "name": {
                        "kind": "Name",
                        "value": "id"
                      },
                      "arguments": [],
                      "directives": []
                    },
                    {
                      "kind": "Field",
                      "name": {
                        "kind": "Name",
                        "value": "name"
                      },
                      "arguments": [],
                      "directives": []
                    }
                  ]
                }
              }
            ]
          }
        }
      ],
      "loc": {
        "start": 0,
        "end": 86,
        "source": {
          "body": "# customInvalidOperation\nquery customInvalidOperation { nonExistentField { id name } }",
          "name": "gql",
          "locationOffset": {
            "line": 1,
            "column": 1
          }
        }
      },
      "__key": 3864687567
    },
    "variables": {},
    "kind": "query",
    "context": {
      "url": "https://rickandmortyapi.com/graphql",
      "preferGetMethod": false,
      "requestPolicy": "cache-first",
      "suspense": false
    }
  }
}

It's not reported by urql this way so we're trying to fetch something
from a field that doesn't exist.

It's stored on `operation.query.definitions[#].name.value`, where
`definitions` is an array.
If multiple definitions can exist with their own operation names, let's
not try to store them all, because our tag value filter doesn't support
substring matches and if more than one operation name is supported it
makes it impossible to filter by it.
@tombruijn tombruijn merged commit ab84ac8 into main Apr 10, 2026
25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature A new feature for this component.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants